package de.bremen.bremenbybike.coordinates;

import java.security.InvalidParameterException;

import android.util.Log;

/**
 * A geographic position transformer.
 * 
 * @author Dirk Wenig (mail@dirkwenig.eu)
 * @version $Revision: 974 $:, $Date: 2012-06-30 21:15:39 +0200 (Sa, 30. Jun 2012) $:
 */
public final class OptimusPrime {
    /*
     * consts
     */
    private static final String TAG = "mariah.util";

    /*
     * methods
     */
    /**
     * Transforms a position from geographic to Gauss-Kr�ger.
     * 
     * @see <a href="http://www.helmutheimeier.privat.t-online.de/kt/javascripts.html">Homepage von Helmut Heimeier - Kartierung</a>
     * 
     * @param geoPos
     *            the geographic position to be transformed
     * @param carPos
     *            the transformed now Gauss-Kr�ger position
     * @throws InvalidParameterException
     *             thrown if position is out of Gauss-Kr�ger bounds
     */
    public static final void geo2gk_Heimeier(GeographicCoordinate geoPos, GaussKruegerCoordinate carPos) throws InvalidParameterException {
        double a, f, pi, c, ex2, ex4, ex6, ex8, e0, e2, e4, e6, br, tan1, tan2, tan4, cos1, cos2, cos3, cos4, cos5, etasq, nd, g, kz, lh, dl, dl2, dl3, dl4, dl5, nk, hw, rw;

        // see below
        if (geoPos.latitude < 46 || geoPos.latitude > 56 || geoPos.longitude < 5 || geoPos.longitude > 16) {
            Log.e(TAG, "transformation from geographic to Gauss-Kr�ger not possible, position out of bounds");
            throw new InvalidParameterException("position out of bounds");
        }

        // see below
        a = 6377397.155;
        f = 3.34277321e-3;
        pi = Math.PI;

        // see below
        c = a / (1 - f);

        // see below
        ex2 = (2 * f - f * f) / ((1 - f) * (1 - f));
        ex4 = ex2 * ex2;
        ex6 = ex4 * ex2;
        ex8 = ex4 * ex4;

        // see below
        e0 = c * (pi / 180) * (1 - 3 * ex2 / 4 + 45 * ex4 /  64 - 175 * ex6 /  256 + 11025 * ex8 / 16384);
        e2 =                c * (- 3 * ex2 / 8 + 15 * ex4 /  32 - 525 * ex6 / 1024 +  2205 * ex8 /  4096);
        e4 =                                c * (15 * ex4 / 256 - 105 * ex6 / 1024 +  2205 * ex8 / 16384);
        e6 =                                              c * ( -  35 * ex6 / 3072 +   315 * ex8 / 12288);

        // see below
        br = geoPos.latitude * pi / 180;

        tan1 = Math.tan(br);
        tan2 = tan1 * tan1;
        tan4 = tan2 * tan2;

        cos1 = Math.cos(br);
        cos2 = cos1 * cos1;
        cos4 = cos2 * cos2;
        cos3 = cos2 * cos1;
        cos5 = cos4 * cos1;

        etasq = ex2 * cos2;

        // see below
        nd = c / Math.sqrt(1 + etasq);

        // see below
        g = e0 * geoPos.latitude + e2 * Math.sin(2 * br) + e4 * Math.sin(4 * br) + e6 * Math.sin(6 * br);

        // see below
        kz = Math.floor((geoPos.longitude + 1.5) / 3);
        lh = kz * 3;
        dl = (geoPos.longitude - lh) * pi / 180;
        dl2 = dl * dl;
        dl4 = dl2 * dl2;
        dl3 = dl2 * dl;
        dl5 = dl4 * dl;

        // see below
        hw = (g + nd * cos2 * tan1 * dl2 / 2 + nd * cos4 * tan1 * (5 - tan2 + 9 * etasq) * dl4 / 24);
        rw = (nd * cos1 * dl + nd * cos3 * (1 - tan2 + etasq) * dl3 / 6 + nd * cos5 * (5 - 18 * tan2 + tan4) * dl5 / 120 + kz * 1e6 + 500000);

        nk = hw - Math.floor(hw);
        if (nk < 0.5)
            hw = Math.floor(hw);
        else
            hw = Math.floor(hw) + 1;

        nk = rw - Math.floor(rw);
        if (nk < 0.5)
            rw = Math.floor(rw);
        else
            rw = Math.floor(rw+1);

        carPos.x = (float)rw;
        carPos.y = (float)hw;
        carPos.z = 0f;
    }

    //    function geo2gk(lp, bp)
    //    {
    //    /* Copyright (c) 2006, HELMUT H. HEIMEIER
    //       Permission is hereby granted, free of charge, to any person obtaining a
    //       copy of this software and associated documentation files (the "Software"),
    //       to deal in the Software without restriction, including without limitation
    //       the rights to use, copy, modify, merge, publish, distribute, sublicense,
    //       and/or sell copies of the Software, and to permit persons to whom the
    //       Software is furnished to do so, subject to the following conditions:
    //       The above copyright notice and this permission notice shall be included
    //       in all copies or substantial portions of the Software.*/
    //
    //    /* Die Funktion wandelt geographische Koordinaten in GK Koordinaten
    //       um. Geographische L�nge lp und Breite bp m�ssen im Potsdam Datum
    //       gegeben sein. Berechnet werden Rechtswert rw und Hochwert hw.
    //       Fehler berichten Sie bitte an Helmut.Heimeier@t-online.de*/
    //
    //       //Geographische L�nge lp und Breite bp im Potsdam Datum
    //       if (lp == "" || bp == "")
    //       {
    //       rw = "";
    //       hw = "";
    //       return;
    //       }
    //       lp = parseFloat(lp);
    //       bp = parseFloat(bp);
    //
    //    // Grenzen des Gauss-Kr�ger-Systems f�r Deutschland 46� N < bp < 55� N, 5� E < lp < 16� E
    //       if (bp < 46 || bp > 56 || lp < 5 || lp > 16)
    //       {
    //       alert("Werte au�erhalb des f�r Deutschland definierten Gauss-Kr�ger-Systems\n"+
    //       " 5� E < LP < 16� E, 46� N < BP < 55� N");
    //       rw = "";
    //       hw = "";
    //       return;
    //       }
    //
    //    // Potsdam Datum
    //    // Gro�e Halbachse a und Abplattung f
    //       a = 6377397.155;
    //       f = 3.34277321e-3;
    //       pi = Math.PI;
    //
    //    // Polkr�mmungshalbmesser c
    //       c = a/(1-f);
    //
    //    // Quadrat der zweiten numerischen Exzentrizit�t
    //       ex2 = (2*f-f*f)/((1-f)*(1-f));
    //       ex4 = ex2*ex2;
    //       ex6 = ex4*ex2;
    //       ex8 = ex4*ex4;
    //
    //    // Koeffizienten zur Berechnung der Meridianbogenl�nge
    //       e0 = c*(pi/180)*(1 - 3*ex2/4 + 45*ex4/64  - 175*ex6/256  + 11025*ex8/16384);
    //       e2 =            c*(  - 3*ex2/8 + 15*ex4/32  - 525*ex6/1024 +  2205*ex8/4096);
    //       e4 =                          c*(15*ex4/256 - 105*ex6/1024 +  2205*ex8/16384);
    //       e6 =                                    c*( -  35*ex6/3072 +   315*ex8/12288);
    //
    //    // Breite in Radianten
    //       br = bp * pi/180;
    //
    //       tan1 = Math.tan(br);
    //       tan2 = tan1*tan1;
    //       tan4 = tan2*tan2;
    //
    //       cos1 = Math.cos(br);
    //       cos2 = cos1*cos1;
    //       cos4 = cos2*cos2;
    //       cos3 = cos2*cos1;
    //       cos5 = cos4*cos1;
    //
    //       etasq = ex2*cos2;
    //
    //    // Querkr�mmungshalbmesser nd
    //       nd = c/Math.sqrt(1 + etasq);
    //
    //    // Meridianbogenl�nge g aus gegebener geographischer Breite bp
    //       g = e0*bp + e2*Math.sin(2*br) + e4*Math.sin(4*br) + e6*Math.sin(6*br);
    //
    //    // L�ngendifferenz dl zum Bezugsmeridian lh
    //       kz = parseInt((lp+1.5)/3);
    //       lh = kz*3;
    //       dl = (lp - lh)*pi/180;
    //       dl2 = dl*dl;
    //       dl4 = dl2*dl2;
    //       dl3 = dl2*dl;
    //       dl5 = dl4*dl;
    //
    //    // Hochwert hw und Rechtswert rw als Funktion von geographischer Breite und L�nge
    //       hw =  (g + nd*cos2*tan1*dl2/2 + nd*cos4*tan1*(5-tan2+9*etasq)*dl4/24);
    //       rw =      (nd*cos1*dl         + nd*cos3*(1-tan2+etasq)*dl3/6 +
    //                   nd*cos5*(5-18*tan2+tan4)*dl5/120 + kz*1e6 + 500000);
    //
    //       nk = hw - parseInt(hw);
    //       if (nk < 0.5) hw = parseInt(hw)
    //       else hw = parseInt(hw) + 1;
    //
    //       nk = rw - parseInt(rw);
    //       if (nk < 0.5) rw = parseInt(rw)
    //       else rw = parseInt(rw+1);
    //       return;
    //    }

    /**
     * Transforms a position from Gauss-Kr�ger to geographic.
     * 
     * @see <a href="http://www.helmutheimeier.privat.t-online.de/kt/javascripts.html">Homepage von Helmut Heimeier - Kartierung</a>
     * 
     * @param carPos
     *            the Gauss-Kr�ger position to be transformed
     * @param geoPos
     *            the transformed now geographic position
     * @throws InvalidParameterException
     *             thrown if position is out of Gauss-Kr�ger bounds
     */
    public static final void gk2geo_Heimeier(GaussKruegerCoordinate carPos, GeographicCoordinate geoPos) throws InvalidParameterException {
        double a, f, pi, c, ex2, ex4, ex6, ex8, e0, f2, f4, f6, sigma, sigmr, bf, br, tan1, tan2, tan4, cos1, cos2, etasq, nd, nd2, nd3, nd4, nd5, nd6, kz, lh, dy, dy2, dy3, dy4, dy5, dy6, b2, b4, b6, l1, l3, l5, bp, lp;

        // see below
        a = 6377397.155;
        f = 3.34277321e-3;
        pi = Math.PI;

        // see below
        c = a / (1 - f);

        // see below
        ex2 = (2 * f - f * f) / ((1 - f) * (1 - f));
        ex4 = ex2 * ex2;
        ex6 = ex4 * ex2;
        ex8 = ex4 * ex4;

        // see below
        e0 = c * (pi / 180) * (1 - 3 * ex2 / 4 + 45 * ex4 /  64 - 175 * ex6 /  256 + 11025 * ex8 / 16384);
        f2 =     (180 / pi) * (    3 * ex2 / 8 -  3 * ex4 /  16 + 213 * ex6 / 2048 -   255 * ex8 /  4096);
        f4 =                       (180 / pi) * (21 * ex4 / 256 -  21 * ex6 /  256 +   533 * ex8 /  8192);
        f6 =                                        (180 / pi) * (151 * ex6 / 6144 -   453 * ex8 / 12288);

        // see below
        sigma = carPos.y / e0;
        sigmr = sigma * pi / 180;
        bf = sigma + f2 * Math.sin(2 * sigmr) + f4 * Math.sin(4 * sigmr) + f6 * Math.sin(6 * sigmr);

        // see below
        br = bf * pi / 180;
        tan1 = Math.tan(br);
        tan2 = tan1 * tan1;
        tan4 = tan2 * tan2;

        cos1 = Math.cos(br);
        cos2 = cos1 * cos1;

        etasq = ex2 * cos2;

        // see below
        nd = c / Math.sqrt(1 + etasq);
        nd2 = nd * nd;
        nd4 = nd2 * nd2;
        nd6 = nd4 * nd2;
        nd3 = nd2 * nd;
        nd5 = nd4 * nd;

        // see below
        kz = Math.floor(carPos.x / 1e6);
        lh = kz * 3;
        dy = carPos.x - (kz * 1e6 + 500000);
        dy2 = dy * dy;
        dy4 = dy2 * dy2;
        dy3 = dy2 * dy;
        dy5 = dy4 * dy;
        dy6 = dy3 * dy3;

        b2 = -tan1 * (1 + etasq) / (2 * nd2);
        b4 = tan1 * (5 + 3 * tan2 + 6 * etasq * (1 - tan2)) / (24 * nd4);
        b6 = -tan1 * (61 + 90 * tan2 + 45 * tan4) / (720 * nd6);

        l1 = 1 / (nd * cos1);
        l3 = -(1 + 2 * tan2 + etasq) / (6 * nd3 * cos1);
        l5 = (5 + 28 * tan2 + 24 * tan4) / (120 * nd5 * cos1);

        // see below
        bp = bf + (180 / pi) * (b2 * dy2 + b4 * dy4 + b6 * dy6);
        lp = lh + (180 / pi) * (l1 * dy  + l3 * dy3 + l5 * dy5);

        if (lp < 5 || lp > 16 || bp < 46 || bp > 56) {
            Log.e(TAG, "transformation from geographic to Gauss-Kr�ger not possible, position out of bounds");
            throw new InvalidParameterException("position out of bounds");
        }

        geoPos.latitude = bp;
        geoPos.longitude = lp;
        geoPos.altitude = 0d;
    }

    //    function gk2geo(rw, hw)
    //    {
    //    /* Copyright (c) 2006, HELMUT H. HEIMEIER
    //       Permission is hereby granted, free of charge, to any person obtaining a
    //       copy of this software and associated documentation files (the "Software"),
    //       to deal in the Software without restriction, including without limitation
    //       the rights to use, copy, modify, merge, publish, distribute, sublicense,
    //       and/or sell copies of the Software, and to permit persons to whom the
    //       Software is furnished to do so, subject to the following conditions:
    //       The above copyright notice and this permission notice shall be included
    //       in all copies or substantial portions of the Software.*/
    //
    //    /* Die Funktion wandelt GK Koordinaten in geographische Koordinaten
    //       um. Rechtswert rw und Hochwert hw m�ssen gegeben sein.
    //       Berechnet werden geographische L�nge lp und Breite bp
    //       im Potsdam Datum.
    //       Fehler berichten Sie bitte an Helmut.Heimeier@t-online.de*/
    //
    //    // Rechtswert rw und Hochwert hw im Potsdam Datum
    //       if (rw == "" || hw == "")
    //       {
    //       lp = "";
    //       bp = "";
    //       return;
    //       }
    //       rw = parseFloat(rw);
    //       hw = parseFloat(hw);
    //
    //       // Potsdam Datum
    //    // Gro�e Halbachse a und Abplattung f
    //       a = 6377397.155;
    //       f = 3.34277321e-3;
    //       pi = Math.PI;
    //
    //    // Polkr�mmungshalbmesser c
    //       c = a/(1-f);
    //
    //    // Quadrat der zweiten numerischen Exzentrizit�t
    //       ex2 = (2*f-f*f)/((1-f)*(1-f));
    //       ex4 = ex2*ex2;
    //       ex6 = ex4*ex2;
    //       ex8 = ex4*ex4;
    //
    //    // Koeffizienten zur Berechnung der geographischen Breite aus gegebener
    //    // Meridianbogenl�nge
    //       e0 = c*(pi/180)*(1 - 3*ex2/4 + 45*ex4/64  - 175*ex6/256  + 11025*ex8/16384);
    //       f2 =   (180/pi)*(    3*ex2/8 -  3*ex4/16  + 213*ex6/2048 -   255*ex8/4096);
    //       f4 =              (180/pi)*(   21*ex4/256 -  21*ex6/256  +   533*ex8/8192);
    //       f6 =                           (180/pi)*(   151*ex6/6144 -   453*ex8/12288);
    //
    //    // Geographische Breite bf zur Meridianbogenl�nge gf = hw
    //       sigma = hw/e0;
    //       sigmr = sigma*pi/180;
    //       bf = sigma + f2*Math.sin(2*sigmr) + f4*Math.sin(4*sigmr) + f6*Math.sin(6*sigmr);
    //
    //    // Breite bf in Radianten
    //       br = bf * pi/180;
    //       tan1 = Math.tan(br);
    //       tan2 = tan1*tan1;
    //       tan4 = tan2*tan2;
    //
    //       cos1 = Math.cos(br);
    //       cos2 = cos1*cos1;
    //
    //       etasq = ex2*cos2;
    //
    //    // Querkr�mmungshalbmesser nd
    //       nd = c/Math.sqrt(1 + etasq);
    //       nd2 = nd*nd;
    //       nd4 = nd2*nd2;
    //       nd6 = nd4*nd2;
    //       nd3 = nd2*nd;
    //       nd5 = nd4*nd;
    //
    //    //  L�ngendifferenz dl zum Bezugsmeridian lh
    //       kz = parseInt(rw/1e6);
    //       lh = kz*3
    //       dy = rw-(kz*1e6+500000);
    //       dy2 = dy*dy;
    //       dy4 = dy2*dy2;
    //       dy3 = dy2*dy;
    //       dy5 = dy4*dy;
    //       dy6 = dy3*dy3;
    //
    //       b2 = - tan1*(1+etasq)/(2*nd2);
    //       b4 =   tan1*(5+3*tan2+6*etasq*(1-tan2))/(24*nd4);
    //       b6 = - tan1*(61+90*tan2+45*tan4)/(720*nd6);
    //
    //       l1 =   1/(nd*cos1);
    //       l3 = - (1+2*tan2+etasq)/(6*nd3*cos1);
    //       l5 =   (5+28*tan2+24*tan4)/(120*nd5*cos1);
    //
    //    // Geographischer Breite bp und L�nge lp als Funktion von Rechts- und Hochwert
    //       bp = bf + (180/pi) * (b2*dy2 + b4*dy4 + b6*dy6);
    //       lp = lh + (180/pi) * (l1*dy  + l3*dy3 + l5*dy5);
    //
    //       if (lp < 5 || lp > 16 || bp < 46 || bp > 56)
    //       {
    //       alert("RW und/oder HW ung�ltig f�r das deutsche Gauss-Kr�ger-System");
    //       lp = "";
    //       bp = "";
    //       }
    //       return;
    //    }

    /**
     * Transforms a geographic position from WGS84 to Potsdam.
     * 
     * @see <a href="http://www.helmutheimeier.privat.t-online.de/kt/javascripts.html">Homepage von Helmut Heimeier - Kartierung</a>
     * 
     * @param geoPosWGS
     *            the geographic position to be transformed
     * @param geoPosPot
     *            the transformed geographic position
     */
    public static final void wgs2pot_Heimeier(GeographicCoordinate geoPosWGS, GeographicCoordinate geoPosPot) {
        double a, fq, f, dx, dy, dz, e2q, e2, pi, b1, l1, nd, xw, yw, zw, x, y, z, rb, b2, l2;

        // see below
        a = 6378137.000;
        fq = 3.35281066e-3;

        // see below
        f = fq - 1.003748e-5;

        // see below
        dx = -587;
        dy = -16;
        dz = -393;

        // see below
        e2q = (2 * fq - fq * fq);
        e2 = (2 * f - f * f);

        // see below
        pi = Math.PI;
        b1 = geoPosWGS.latitude * (pi / 180);
        l1 = geoPosWGS.longitude * (pi / 180);

        // see below
        nd = a / Math.sqrt(1 - e2q * Math.sin(b1) * Math.sin(b1));

        // see below
        xw = nd * Math.cos(b1) * Math.cos(l1);
        yw = nd * Math.cos(b1) * Math.sin(l1);
        zw = (1 - e2q) * nd * Math.sin(b1);

        // see below
        x = xw + dx;
        y = yw + dy;
        z = zw + dz;

        // see below
        rb = Math.sqrt(x * x + y * y);
        b2 = (180 / pi) * Math.atan((z / rb) / (1 - e2));

        l2 = 0;
        if (x > 0)
            l2 = (180 / pi) * Math.atan(y / x);
        if (x < 0 && y > 0)
            l2 = (180 / pi) * Math.atan(y / x) + 180;
        if (x < 0 && y < 0)
            l2 = (180 / pi) * Math.atan(y / x) - 180;

        geoPosPot.latitude = b2;
        geoPosPot.longitude = l2;
        geoPosPot.altitude = 0d;
    }

    //    function wgs2pot(lw, bw)
    //    {
    //    /* Copyright (c) 2006, HELMUT H. HEIMEIER
    //       Permission is hereby granted, free of charge, to any person obtaining a
    //       copy of this software and associated documentation files (the "Software"),
    //       to deal in the Software without restriction, including without limitation
    //       the rights to use, copy, modify, merge, publish, distribute, sublicense,
    //       and/or sell copies of the Software, and to permit persons to whom the
    //       Software is furnished to do so, subject to the following conditions:
    //       The above copyright notice and this permission notice shall be included
    //       in all copies or substantial portions of the Software.*/
    //
    //    /* Die Funktion verschiebt das Kartenbezugssystem (map datum) vom
    //       WGS84 Datum (World Geodetic System 84) zum in Deutschland
    //       gebr�uchlichen Potsdam-Datum. Geographische L�nge lw und Breite
    //       bw gemessen in grad auf dem WGS84 Ellipsoid m�ssen
    //       gegeben sein. Ausgegeben werden geographische L�nge lp
    //       und Breite bp (in grad) auf dem Bessel-Ellipsoid.
    //       Bei der Transformation werden die Ellipsoidachsen parallel
    //       verschoben um dx = -587 m, dy = -16 m und dz = -393 m.
    //       Fehler berichten Sie bitte an Helmut.Heimeier@t-online.de*/
    //
    //    // Geographische L�nge lw und Breite bw im WGS84 Datum
    //       if (lw == "" || bw == "")
    //       {
    //       lp = "";
    //       bp = "";
    //       return;
    //       }
    //       lw = parseFloat(lw);
    //       bw = parseFloat(bw);
    //
    //    // Quellsystem WGS 84 Datum
    //    // Gro�e Halbachse a und Abplattung fq
    //       a = 6378137.000;
    //       fq = 3.35281066e-3;
    //
    //    // Zielsystem Potsdam Datum
    //    // Abplattung f
    //       f = fq - 1.003748e-5
    //
    //    // Parameter f�r datum shift
    //       dx = -587;
    //       dy = -16;
    //       dz = -393;
    //
    //    // Quadrat der ersten numerischen Exzentrizit�t in Quell- und Zielsystem
    //       e2q = (2*fq-fq*fq);
    //       e2 = (2*f-f*f);
    //
    //    // Breite und L�nge in Radianten
    //       pi = Math.PI;
    //       b1 = bw * (pi/180);
    //       l1 = lw * (pi/180);
    //
    //    // Querkr�mmungshalbmesser nd
    //       nd = a/Math.sqrt(1 - e2q*Math.sin(b1)*Math.sin(b1));
    //
    //    // Kartesische Koordinaten des Quellsystems WGS84
    //       xw = nd*Math.cos(b1)*Math.cos(l1);
    //       yw = nd*Math.cos(b1)*Math.sin(l1);
    //       zw = (1 - e2q)*nd*Math.sin(b1);
    //
    //       // Kartesische Koordinaten des Zielsystems (datum shift) Potsdam
    //       x = xw + dx;
    //       y = yw + dy;
    //       z = zw + dz;
    //
    //    // Berechnung von Breite und L�nge im Zielsystem
    //       rb = Math.sqrt(x*x + y*y);
    //       b2 = (180/pi) * Math.atan((z/rb)/(1-e2));
    //
    //       if (x > 0)
    //         l2 = (180/pi) * Math.atan(y/x);
    //       if (x < 0 && y > 0)
    //         l2 = (180/pi) * Math.atan(y/x) + 180;
    //       if (x < 0 && y < 0)
    //         l2 = (180/pi) * Math.atan(y/x) - 180;
    //
    //       lp = l2;
    //       bp = b2;
    //
    //       if (lp < 5 || lp > 16 || bp < 46 || bp > 56)
    //       {
    //       lp = "";
    //       bp = "";
    //       }
    //       return;
    //    }

    /**
     * Transforms a geographic position from Potsdam to WGS84.
     * 
     * @see <a href="http://www.helmutheimeier.privat.t-online.de/kt/javascripts.html">Homepage von Helmut Heimeier - Kartierung</a>
     * 
     * @param geoPosPot
     *            the geographic position to be transformed
     * @param geoPosWGS
     *            the transformed geographic position
     */
    public static final void pot2wgs_Heimeier(GeographicCoordinate geoPosPot, GeographicCoordinate geoPosWGS) {
        double a, fq, f, dx, dy, dz, e2q, e2, pi, b1, l1, nd, xp, yp, zp, x, y, z, rb, b2, l2;

        // see below
        a = 6378137.000 - 739.845;
        fq = 3.35281066e-3 - 1.003748e-05;

        // see below
        f = 3.35281066e-3;

        // see below
        dx = 587;
        dy = 16;
        dz = 393;

        // see below
        e2q = (2 * fq - fq * fq);
        e2 = (2 * f - f * f);

        // see below
        pi = Math.PI;
        b1 = geoPosPot.latitude * (pi / 180);
        l1 = geoPosPot.longitude * (pi / 180);

        // see below
        nd = a / Math.sqrt(1 - e2q * Math.sin(b1) * Math.sin(b1));

        // see below
        xp = nd * Math.cos(b1) * Math.cos(l1);
        yp = nd * Math.cos(b1) * Math.sin(l1);
        zp = (1 - e2q) * nd * Math.sin(b1);

        // see below
        x = xp + dx;
        y = yp + dy;
        z = zp + dz;

        // see below
        rb = Math.sqrt(x * x + y * y);
        b2 = (180 / pi) * Math.atan((z / rb) / (1 - e2));

        l2 = 0f;
        if (x > 0)
            l2 = (180 / pi) * Math.atan(y / x);
        if (x < 0 && y > 0)
            l2 = (180 / pi) * Math.atan(y / x) + 180;
        if (x < 0 && y < 0)
            l2 = (180 / pi) * Math.atan(y / x) - 180;

        geoPosWGS.latitude = b2;
        geoPosWGS.longitude = l2;
        geoPosWGS.altitude = 0d;
    }

    //    function pot2wgs(lp, bp)
    //    {
    //    /* Copyright (c) 2006, HELMUT H. HEIMEIER
    //       Permission is hereby granted, free of charge, to any person obtaining a
    //       copy of this software and associated documentation files (the "Software"),
    //       to deal in the Software without restriction, including without limitation
    //       the rights to use, copy, modify, merge, publish, distribute, sublicense,
    //       and/or sell copies of the Software, and to permit persons to whom the
    //       Software is furnished to do so, subject to the following conditions:
    //       The above copyright notice and this permission notice shall be included
    //       in all copies or substantial portions of the Software.*/
    //
    //    /* Die Funktion verschiebt das Kartenbezugssystem (map datum) vom in
    //       Deutschland gebr�uchlichen Potsdam-Datum zum WGS84 (World Geodetic
    //       System 84) Datum. Geographische L�nge lp und Breite bp gemessen in
    //       grad auf dem Bessel-Ellipsoid m�ssen gegeben sein.
    //       Ausgegeben werden geographische L�nge lw und
    //       Breite bw (in grad) auf dem WGS84-Ellipsoid.
    //       Bei der Transformation werden die Ellipsoidachsen parallel
    //       verschoben um dx = 587 m, dy = 16 m und dz = 393 m.
    //       Fehler berichten Sie bitte an Helmut.Heimeier@t-online.de*/
    //
    //    // Geographische L�nge lp und Breite bp im Potsdam Datum
    //       if (lp == "" || bp == "")
    //       {
    //       lw = "";
    //       bw = "";
    //       return;
    //       }
    //
    //       lp = parseFloat(lp);
    //       bp = parseFloat(bp);
    //
    //    // Quellsystem Potsdam Datum
    //    //  Gro�e Halbachse a und Abplattung fq
    //       a = 6378137.000 - 739.845;
    //       fq = 3.35281066e-3 - 1.003748e-05;
    //
    //    // Zielsystem WGS84 Datum
    //    //  Abplattung f
    //       f = 3.35281066e-3;
    //
    //    // Parameter f�r datum shift
    //       dx = 587;
    //       dy = 16;
    //       dz = 393;
    //
    //    // Quadrat der ersten numerischen Exzentrizit�t in Quell- und Zielsystem
    //       e2q = (2*fq-fq*fq);
    //       e2 = (2*f-f*f);
    //
    //    // Breite und L�nge in Radianten
    //       pi = Math.PI;
    //       b1 = bp * (pi/180);
    //       l1 = lp * (pi/180);
    //
    //    // Querkr�mmungshalbmesser nd
    //       nd = a/Math.sqrt(1 - e2q*Math.sin(b1)*Math.sin(b1));
    //
    //    // Kartesische Koordinaten des Quellsystems Potsdam
    //       xp = nd*Math.cos(b1)*Math.cos(l1);
    //       yp = nd*Math.cos(b1)*Math.sin(l1);
    //       zp = (1 - e2q)*nd*Math.sin(b1);
    //
    //    // Kartesische Koordinaten des Zielsystems (datum shift) WGS84
    //       x = xp + dx;
    //       y = yp + dy;
    //       z = zp + dz;
    //
    //    // Berechnung von Breite und L�nge im Zielsystem
    //       rb = Math.sqrt(x*x + y*y);
    //       b2 = (180/pi) * Math.atan((z/rb)/(1-e2));
    //
    //       if (x > 0)
    //         l2 = (180/pi) * Math.atan(y/x);
    //       if (x < 0 && y > 0)
    //         l2 = (180/pi) * Math.atan(y/x) + 180;
    //       if (x < 0 && y < 0)
    //         l2 = (180/pi) * Math.atan(y/x) - 180;
    //
    //       lw = l2;
    //       bw = b2;
    //       return;
    //    }

    /**
     * Transforms a position from geographic to Gauss-Kr�ger.
     * 
     * @see <a href="http://www.delphi-treff.de/tipps/mathematik/einheiten/geographische-in-gauss-krueger-koordinaten-umrechnen/">Delphi-Treff - Geographische in Gau�-Kr�ger-Koordinaten umrechnen</a>
     * 
     * @param geoPos
     *            the geographic position to be transformed
     * @param sy
     *            the meridian number
     * @param carPos
     *            the transformed now Gauss-Kr�ger position
     */
    public static final void geo2gk_DelphiTreff(GeographicCoordinate geoPos, int sy, GaussKruegerCoordinate carPos) {
        // 26
        double rho = 180d / Math.PI;

        double brDezimal, laDezimal, rm, e2, c, bf, g, co, g2, g1, t, dl, fa, x, y;

        // 25
        e2 = 0.0067192188d;
        // 27
        c = 6398786.849d;
        brDezimal = geoPos.latitude;
        laDezimal = geoPos.longitude;

        // 64
        bf = brDezimal / rho;
        // 65
        g = 111120.61962d * brDezimal - 15988.63853d * Math.sin(2d * bf) + 16.72995d * Math.sin(4d * bf) - 0.02178d * Math.sin(6d * bf) + 0.00003d * Math.sin(8d * bf);
        // 70
        co = Math.cos(bf);
        // 71
        g2 = e2 * (co * co);
        // 72
        g1 = c / Math.sqrt(1d + g2);
        // 73
        t = Math.sin(bf) / Math.cos(bf); // =tan(t)
        // 74
        dl = laDezimal - sy * 3d;
        // 77
        fa = co * dl / rho;
        // 78
        y = g + fa * fa * t * g1 / 2d + fa * fa * fa * fa * t * g1 * (5d - t * t + 9d * g2) / 24d;
        // 81
        rm = fa * g1 + fa * fa * fa * g1 * (1d - t * t + g2) / 6d + fa * fa * fa * fa * fa * g1 * (5d - 18d * t * t * t * t * t * t) / 120d;
        // 84
        x = rm + sy * 1000000d + 500000d;

        carPos.x = (float)x;
        carPos.y = (float)y;
        carPos.z = 0f;
    }

    //  type Twinkel = record
    //    grad : integer;
    //    min : integer;
    //    sek : integer;
    //  end;
    //
    //  {==============================================================================}
    //  { Umrechnung von Geographischen Koordinaten in Gauss-Krueger-Koordinaten       }
    //  { Formel: Grossmann,W., Geod�tische Abbildungen, 1964, Seite 151               }
    //  { Parameter: geo.Breite (Grad.Min.Sek) in Altgrad  : Twinkel                   }
    //  {            geo.Laenge (Grad.Min.Sek) in Altgrad  : Twinkel                   }
    //  {            Zielsystemnummer (Meridiankennziffer) : longint                   }
    //  {            Rechtswert (X) im Zielsystem          : double                    }
    //  {            Hochwert (Y) im Zielsystem            : double                    }
    //  {==============================================================================}
    //  procedure GeoGk(br,la:Twinkel;sy:Longint;var x,y:double);
    //  const
    //    {26}
    //    rho = 180 / pi;
    //  var
    //    brDezimal,laDezimal,rm,e2,c,bf,g,co,g2,g1,t,dl,fa,grad,min,sek :extended;
    //  begin
    //    {25}
    //    e2 := 0.0067192188;
    //    {27}
    //    c := 6398786.849;
    //    {in Dezimal}
    //    {Breite}
    //    brDezimal := br.grad + br.min / 60 + br.sek / 3600;
    //    {Laenge}
    //    laDezimal := la.grad + la.min / 60 + la.sek / 3600;
    //    {64}
    //    bf := brDezimal / rho;
    //    {65}
    //    g := 111120.61962 * brDezimal
    //         -15988.63853 * sin(2*bf)
    //         +16.72995 * sin(4*bf)
    //         -0.02178 * sin(6*bf)
    //         +0.00003 * sin(8*bf);
    //    {70}
    //    co := cos(bf);
    //    {71}
    //    g2 := e2 * (co * co);
    //    {72}
    //    g1 := c / sqrt(1+g2);
    //    {73}
    //    t := sin(bf) / cos(bf); {=tan(t)}
    //    {74}
    //    dl := laDezimal - sy * 3;
    //    {77}
    //    fa := co * dl / rho;
    //    {78}
    //    y := g
    //         + fa * fa * t * g1 / 2
    //         + fa * fa * fa * fa * t * g1 * (5 - t * t + 9 * g2) / 24;
    //    {81}
    //    rm := fa * g1
    //          + fa * fa * fa * g1 * (1 - t * t + g2) / 6
    //          + fa * fa * fa * fa * fa * g1 * (5 - 18 * t * t * t * t * t * t) / 120;
    //    {84}
    //    x := rm + sy * 1000000 + 500000;
    //  end;

    /**
     * Transforms a position from Gauss-Kr�ger to geographic.
     * 
     * @see <a href="http://www.delphi-treff.de/tipps/mathematik/einheiten/geographische-in-gauss-krueger-koordinaten-umrechnen/">Delphi-Treff - Geographische in Gau�-Kr�ger-Koordinaten umrechnen</a>
     * 
     * @param carPos
     *            the Gauss-Kr�ger position to be transformed
     * @param geoPos
     *            the transformed now geographic position
     */
    public static final void gk2geo_DelphiTreff(GaussKruegerCoordinate carPos, GeographicCoordinate geoPos) {
        // 26
        double rho = 180d / Math.PI;

        float rw = carPos.x;
        float hw = carPos.y;

        double rm, e2, c, bI, bII, bf, co, g2, g1, t, fa, dl, gb, gl;
        int mKen;

        // 25
        e2 = 0.0067192188d;
        // 27
        c = 6398786.849d;
        // 32
        mKen = (int)Math.floor(rw / 1000000f);
        // 33
        rm = rw - mKen * 1000000d - 500000d;
        // 34
        bI = hw / 10000855.7646d;
        // 35
        bII = bI * bI;
        // 36
        bf = 325632.08677d * bI *((((((0.00000562025d * bII - 0.00004363980d) * bII + 0.00022976983d) * bII - 0.00113566119d) * bII + 0.00424914906d) * bII - 0.00831729565d) * bII + 1d);
        // 43
        bf = bf / 3600d / rho;
        // 44
        co = Math.cos(bf);
        // 45
        g2 = e2 * (co * co);
        // 46
        g1 = c / Math.sqrt(1d + g2);
        // 47
        t = Math.sin(bf) / Math.cos(bf); // =tan(bf)
        // 51
        fa = rm / g1;
        // 52
        gb = bf - fa * fa * t * (1d + g2) / 2d + fa * fa * fa * fa * t * (5d + 3d * t * t + 6d * g2 - 6d * g2 * t * t) / 24d;
        // 55
        gb = gb * rho;
        // 56
        dl = fa - fa * fa * fa * (1d + 2d * t * t + g2) / 6d + fa * fa * fa * fa * fa * (1d + 28d * t * t + 24d * t * t * t * t) / 120d;
        // 59
        gl = dl *rho / co + mKen * 3d;

        geoPos.latitude = gb;
        geoPos.longitude = gl;
        geoPos.altitude = 0d;
    }

    //type Twinkel = record
    //  grad : integer;
    //  min : integer;
    //  sek : integer;
    //end;
    //
    //{==============================================================================}
    //{ Umrechnung von Geographischen Koordinaten in Gauss-Krueger-Koordinaten       }
    //{ Formel: Grossmann,W., Geod�tische Abbildungen, 1964, Seite 153               }
    //{ Parameter: Rechtswert                            : double                    }
    //{            Hochwert                              : double                    }
    //{            geo.Breite (Grad.Min.Sek) in Altgrad  : Twinkel                   }
    //{            geo.Laenge (Grad.Min.Sek) in Altgrad  : Twinkel                   }
    //{==============================================================================}
    //procedure GkGeo(rw,hw:double;var br,la:Twinkel);
    //const
    //  {26}
    //  rho= 180 / pi;
    //var
    //  rm,e2,c,bI,bII,bf,co,g2,g1,t,fa,dl,gb,gl,grad,min,sek :extended;
    //  mKen :integer;
    //begin
    //  {25}
    //  e2 := 0.0067192188;
    //  {27}
    //  c := 6398786.849;
    //  {32}
    //  mKen := trunc(rw / 1000000);
    //  {33}
    //  rm := rw - mKen * 1000000 - 500000;
    //  {34}
    //  bI := hw / 10000855.7646;
    //  {35}
    //  bII := bI * bI;
    //  {36}
    //  bf := 325632.08677 *bI *((((((0.00000562025
    //        * bII - 0.00004363980)
    //        * bII + 0.00022976983)
    //        * bII - 0.00113566119)
    //        * bII + 0.00424914906)
    //        * bII - 0.00831729565)
    //        * bII + 1);
    //  {43}
    //  bf := bf / 3600 / rho;
    //  {44}
    //  co := cos(bf);
    //  {45}
    //  g2 := e2 * (co * co);
    //  {46}
    //  g1 := c / sqrt(1 + g2);
    //  {47}
    //  t := sin(bf) / cos(bf); {=tan(bf)}
    //  {51}
    //  fa := rm / g1;
    //  {52}
    //  gb := bf
    //        - fa * fa * t * (1 + g2) / 2
    //        + fa * fa * fa * fa * t * (5 + 3 * t * t + 6 * g2 - 6 * g2 * t * t) / 24;
    //  {55}
    //  gb := gb * rho;
    //  {56}
    //  dl := fa
    //        - fa * fa * fa * (1 + 2 * t * t + g2) / 6
    //        + fa * fa * fa * fa * fa * (1 + 28 * t * t + 24 * t * t * t * t) / 120;
    //  {59}
    //  gl := dl *rho / co + mKen * 3;
    //  {in grad.min.sek}
    //  {Breite}
    //  grad:=int(gb);
    //   sek:=60*(gb-grad);
    //   min:=int(sek);
    //   sek:=60*(sek-min);
    //  br.grad := trunc(grad);
    //  br.min  := trunc(min);
    //  br.sek  := trunc(sek);
    //  {Laenge}
    //  grad:=int(gl);
    //   sek:=60*(gl-grad);
    //   min:=int(sek);
    //   sek:=60*(sek-min);
    //  la.grad := trunc(grad);
    //  la.min  := trunc(min);
    //  la.sek  := trunc(sek);
    //end;
}
